Skip to content

Conversation

@earthling-amzn
Copy link
Contributor

@earthling-amzn earthling-amzn commented Oct 24, 2025

When GenShen is only marking the old generation, we do not need the SATB mechanism to preserve young pointers. We currently filter these out of the SATB buffers during the final-update-refs and init-mark safepoints. This increases latency and introduces no small amount of complexity. It should be possible to instead filter out these pointers when the SATB buffers are 'compacted' before being 'completed'.

Background

When GenShen is marking the old generation it leaves the SATB barrier enabled. When a young collection interrupts old marking, it creates a situation where a mutator thread could overwrite a field holding a pointer into a collection set region. The SATB barrier will dutifully place this object in the SATB queue. If this pointer makes it into a mark queue, the marking thread will crash. Prior to this change, GenShen filtered out such pointers after the thread local SATB buffers were completed. After this change, such pointers are filtered out before the buffers are completed. This is more inline with the natural way of things.


Progress

  • Change must be properly reviewed (1 review required, with at least 1 Reviewer)
  • Change must not contain extraneous whitespace
  • Commit message must refer to an issue

Issue

  • JDK-8370041: GenShen: Filter young pointers from thread local SATB buffers when only marking old (Enhancement - P4)

Reviewers

Reviewing

Using git

Checkout this PR locally:
$ git fetch https://git.openjdk.org/jdk.git pull/27983/head:pull/27983
$ git checkout pull/27983

Update a local copy of the PR:
$ git checkout pull/27983
$ git pull https://git.openjdk.org/jdk.git pull/27983/head

Using Skara CLI tools

Checkout this PR locally:
$ git pr checkout 27983

View PR using the GUI difftool:
$ git pr show -t 27983

Using diff file

Download this PR as a diff file:
https://git.openjdk.org/jdk/pull/27983.diff

Using Webrev

Link to Webrev Comment

@bridgekeeper
Copy link

bridgekeeper bot commented Oct 24, 2025

👋 Welcome back wkemper! A progress list of the required criteria for merging this PR into master will be added to the body of your pull request. There are additional pull request commands available for use with this pull request.

@openjdk
Copy link

openjdk bot commented Oct 24, 2025

@earthling-amzn This change now passes all automated pre-integration checks.

ℹ️ This project also has non-automated pre-integration requirements. Please see the file CONTRIBUTING.md for details.

After integration, the commit message for the final commit will be:

8370041: GenShen: Filter young pointers from thread local SATB buffers when only marking old

Reviewed-by: kdnilsen, ysr

You can use pull request commands such as /summary, /contributor and /issue to adjust it as needed.

At the time when this comment was updated there had been 51 new commits pushed to the master branch:

As there are no conflicts, your changes will automatically be rebased on top of these commits when integrating. If you prefer to avoid this automatic rebasing, please check the documentation for the /integrate command for further details.

➡️ To integrate this PR with the above commit message to the master branch, type /integrate in a new comment.

@openjdk openjdk bot added hotspot-gc hotspot-gc-dev@openjdk.org shenandoah shenandoah-dev@openjdk.org labels Oct 24, 2025
@openjdk
Copy link

openjdk bot commented Oct 24, 2025

@earthling-amzn The following labels will be automatically applied to this pull request:

  • hotspot-gc
  • shenandoah

When this pull request is ready to be reviewed, an "RFR" email will be sent to the corresponding mailing lists. If you would like to change these labels, use the /label pull request command.

@openjdk openjdk bot added the rfr Pull request is ready for review label Oct 24, 2025
@mlbridge
Copy link

mlbridge bot commented Oct 24, 2025

Webrevs

Copy link
Contributor

@kdnilsen kdnilsen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a great simplification. Do we have any performance numbers, especially for reduction in p99.999 and p100 latencies with certain Extremem workloads, which I believe to be related to safepoint flushing of satb buffers?

// be in the collection set. If this happens, the pointer will be preserved, essentially
// becoming part of the old snapshot.
// 2. The region is allocated during evacuation of old. This is also not a concern because
// we haven't yet finished marking old so no mixed evacuations will happen.
Copy link
Contributor

@kdnilsen kdnilsen Oct 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might be worth mentioning that there may be some additional analysis and code required when we use the forwarding table to recycle cset regions during evacuation and/or updating. If one of these regions becomes old before the SATB buffers have been flushed, then a young cset pointer that lingers in a SATB buffer will "all of a sudden" look like a valid old pointer and will not be purged from the SATB buffer. When we subsequently scan the "object" referenced by this obsolete pointer, we are likely to find "garbage memory", possibly resulting in a crash.

I am thinking that an initial fix might be to do this flushing at init-update-refs instead of at final update refs, and to not recycle cset regions until evacuation is all done. Is there a different handshake there that we might piggyback on?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, that's a good point about recycling regions concurrently. I don't think we can flush before init-update-refs because forwarded pointers still exist and the SATB barrier doesn't try to resolve them. That is to say, "bad" pointers can be created throughout the update-refs phase.

Even in the (hypothetical) scenario with a forwarding table, a region would only become old through an in-place-promotion (in which case, it will never have been recycled) or we will be doing a mixed evacuation. If we are running a mixed evacuation, we will already have finished marking old.

@earthling-amzn
Copy link
Contributor Author

I have results from an earlier version of this PR that flushed the buffers during init-mark. That version showed a consistent 3% improvement on critical and max jops. They also showed a reduction in p99.999 latency for some of the extremem measurements. I will retest with this version.

@earthling-amzn earthling-amzn marked this pull request as draft October 28, 2025 16:09
@earthling-amzn
Copy link
Contributor Author

I found an issue with the verifier that needs to be fixed before this PR is integrated.

@openjdk openjdk bot removed the rfr Pull request is ready for review label Oct 28, 2025
…s in progress

This has to happen at least once during the degenerated cycle. Doing it at the start, rather than the end, simplifies the verifier.
@earthling-amzn earthling-amzn marked this pull request as ready for review October 30, 2025 23:59
@openjdk openjdk bot added the rfr Pull request is ready for review label Oct 31, 2025
@earthling-amzn
Copy link
Contributor Author

I've run more tests and confirmed that critical and max jops are 3% improved on a variety of heap sizes and configurations. Additionally, after running more tests with extremem, the apparent regression at p100 has evaporated:

genshen/extremem/control                                                                                                                                                                                                  
                                Category |  Count |         Total |      GeoMean |      Average |     Trim 0.1 |       StdDev |      Minimum |      Maximum                                                               
                  sales_transaction_p100 |     23 |     38817.000 |     1685.651 |     1687.696 |     1690.158 |       84.299 |     1539.000 |     1823.000                                                               
                   browsing_history_p100 |     23 |     32145.000 |     1391.269 |     1397.609 |     1382.211 |      139.779 |     1175.000 |     1769.000                                                               
               customer_replacement_p100 |     23 |    107141.000 |     4652.940 |     4658.304 |     4664.211 |      227.279 |     4093.000 |     5053.000                                                               
                product_replacement_p100 |     23 |     58315.000 |     2526.181 |     2535.435 |     2523.737 |      223.834 |     2203.000 |     3041.000                                                               
               customer_preparation_p100 |     23 |    142953.000 |     5442.520 |     6215.348 |     6064.000 |     3132.333 |     2502.000 |    11547.000                                                               
                  customer_purchase_p100 |     23 |    491201.000 |    18622.792 |    21356.565 |    20385.263 |    11424.057 |     6582.000 |    48274.000                                                               
            customer_save_for_later_p100 |     23 |    880105.000 |    36675.774 |    38265.435 |    37248.895 |    11777.757 |    23023.000 |    65591.000                                                               
               customer_abandonment_p100 |     23 |    701197.000 |    28578.563 |    30486.826 |    29311.105 |    11521.080 |    16390.000 |    59179.000                                                               
genshen/extremem/experiment                                                                                                                                                                                               
                                Category |  Count |         Total |      GeoMean |      Average |     Trim 0.1 |       StdDev |      Minimum |      Maximum                                                               
                  sales_transaction_p100 |     23 |     40007.000 |     1730.529 |     1739.435 |     1711.947 |      193.896 |     1528.000 |     2490.000                                                               
                   browsing_history_p100 |     23 |     33032.000 |     1425.803 |     1436.174 |     1436.316 |      175.753 |     1123.000 |     1729.000                                                               
               customer_replacement_p100 |     23 |    107516.000 |     4653.546 |     4674.609 |     4599.579 |      488.862 |     4072.000 |     6579.000                                                               
                product_replacement_p100 |     23 |     56647.000 |     2451.855 |     2462.913 |     2469.789 |      235.896 |     1968.000 |     2903.000
               customer_preparation_p100 |     23 |    136482.000 |     5224.974 |     5934.000 |     5766.684 |     3027.151 |     2924.000 |    10652.000
                  customer_purchase_p100 |     23 |    464888.000 |    17675.074 |    20212.522 |    18641.263 |    11355.233 |     7921.000 |    55420.000
            customer_save_for_later_p100 |     23 |    854932.000 |    35744.969 |    37170.957 |    35660.579 |    11323.562 |    24370.000 |    71376.000
               customer_abandonment_p100 |     23 |    686923.000 |    28261.963 |    29866.217 |    28589.737 |    10907.943 |    17791.000 |    65329.000

Indeed, the experiment looks slightly better in some cases (slightly worse in others). The results also show the expected reduction in safepoint times as we are no longer flushing SATB buffers during final_update_refs or init_mark:

-133.33% extremem/shenandoahfinalupdaterefs_stopped_max p=0.00000 (Welch's T-Test)
  Control:      1.103   (+/-  0.14  )         23
  Test:         0.473   (+/-  0.06  )         23

The effect is more pronounced on specjbb2015:

-216.54% specjbb2015/shenandoahfinalupdaterefs_stopped_max p=0.00000 (Mann-Whitney)                                                                                                                                                 
  Control:      2.217   (+/-  0.09  )         22                                                                                                                                                                          
  Test:         0.700   (+/-  0.19  )         22 

-791.43% specjbb2015/shenandoahinitmark_stopped_max p=0.00173 (Mann-Whitney)                                                                                                                                                        
  Control:      3.408   (+/-  3.12  )         22                                                                                                                                                                          
  Test:         0.382   (+/-  0.07  )         22

Copy link
Contributor

@kdnilsen kdnilsen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for bringing this to closure...

Copy link
Member

@ysramakrishna ysramakrishna left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changes look ok. I found some of the comments confusing -- I have left some remarks at those places, please have a look to see if they can be made clearer.

The improvement in performance looks good. Do we track the number of SATB pointers processed by the old marking (to compare between before and after your changes here)?

Comment on lines +1140 to +1143
// 1. The region is promoted in place. This is safe because such regions will never
// be in the collection set. If this happens, the pointer will be preserved, essentially
// becoming part of the old snapshot.
// 2. The region is allocated during evacuation of old. This is also not a concern because
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One related question. In both these cases, I assume the reference will look "marked" because it's above TAMS for the purposes of the old marking?

// We leave the SATB barrier on for the entirety of the old generation
// marking phase. In some cases, this can cause a write to a perfectly
// reachable oop to enqueue a pointer that later becomes garbage (because
// it points at an object that is later chosen for the collection set). There are
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  // ... In some cases, this can cause a write to a perfectly
  // reachable oop to enqueue a pointer that later becomes garbage (because
  // it points at an object that is later chosen for the collection set).

I don't understand this statement. The SATB is supposed to be pointers to objects that we will preserve because they were reachable when the snapshot (marking) was started. Can you elaborate what you mean here? Did you mean that the filtering of the SATB didn't filter a (sometime) young reference which was then processed by the old marking?

Comment on lines 235 to 237
// also cases where the referent of a weak reference ends up in the SATB
// and is later collected. In these cases the oop in the SATB buffer becomes
// invalid and the _next_ cycle will crash during its marking phase. To
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again I don't understand the concept of an SATB pointer to an object that was later collected? Are we talking about young objects that are subsequently processed by old marking because they weren't filtered out when they should be?

I think that is probably the case here, but it would be good to clean up these comments to avoid this confusion.

@openjdk openjdk bot added the ready Pull request is ready to be integrated label Nov 6, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

hotspot-gc hotspot-gc-dev@openjdk.org ready Pull request is ready to be integrated rfr Pull request is ready for review shenandoah shenandoah-dev@openjdk.org

Development

Successfully merging this pull request may close these issues.

3 participants